<template>
  <view class="image-upload" :class="uploadClass">
    <!-- 已上传图片列表 -->
    <view class="image-upload__list">
      <view
        v-for="(item, index) in fileList"
        :key="index"
        class="image-upload__item"
        :style="itemStyle"
      >
        <!-- 图片 -->
        <image
          class="image-upload__image"
          :src="item.url"
          :mode="imageMode"
          @click="handlePreview(item, index)"
        />

        <!-- 遮罩层 -->
        <view class="image-upload__mask">
          <!-- 预览按钮 -->
          <view class="image-upload__action" @click="handlePreview(item, index)">
            <text class="image-upload__action-icon">👁</text>
          </view>

          <!-- 删除按钮 -->
          <view
            v-if="!disabled && deletable"
            class="image-upload__action image-upload__action--danger"
            @click="handleRemove(index)"
          >
            <text class="image-upload__action-icon">🗑</text>
          </view>
        </view>

        <!-- 上传状态 -->
        <view v-if="item.status === 'uploading'" class="image-upload__loading">
          <view class="image-upload__loading-spinner"></view>
          <text class="image-upload__loading-text">{{ item.progress }}%</text>
        </view>

        <!-- 错误状态 -->
        <view v-if="item.status === 'error'" class="image-upload__error">
          <text class="image-upload__error-text">上传失败</text>
        </view>
      </view>

      <!-- 上传按钮 -->
      <view
        v-if="showUploadBtn"
        class="image-upload__upload"
        :style="itemStyle"
        @click="handleUpload"
      >
        <text class="image-upload__upload-icon">+</text>
        <text class="image-upload__upload-text">{{ uploadText }}</text>
      </view>
    </view>

    <!-- 图片预览 -->
    <view v-if="previewVisible" class="image-preview" @click="handleClosePreview">
      <swiper
        class="image-preview__swiper"
        :current="previewIndex"
        :indicator-dots="true"
        :autoplay="false"
        @change="handleSwiperChange"
      >
        <swiper-item
          v-for="(item, index) in fileList"
          :key="index"
          class="image-preview__item"
        >
          <image
            class="image-preview__image"
            :src="item.url"
            mode="aspectFit"
            @click.stop
          />
        </swiper-item>
      </swiper>

      <!-- 预览页码 -->
      <view class="image-preview__counter">
        {{ previewIndex + 1 }} / {{ fileList.length }}
      </view>
    </view>
  </view>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'

// 文件项接口
interface FileItem {
  url: string
  name?: string
  size?: number
  status: 'ready' | 'uploading' | 'success' | 'error'
  progress?: number
  error?: string
}

// Props
interface Props {
  modelValue?: FileItem[]
  accept?: string
  maxSize?: number
  maxCount?: number
  disabled?: boolean
  deletable?: boolean
  preview?: boolean
  imageMode?: 'scaleToFill' | 'aspectFit' | 'aspectFill' | 'widthFix' | 'heightFix'
  itemSize?: string
  uploadText?: string
  customClass?: string
}

const props = withDefaults(defineProps<Props>(), {
  modelValue: () => [],
  accept: 'image/*',
  maxSize: 10 * 1024 * 1024, // 10MB
  maxCount: 9,
  disabled: false,
  deletable: true,
  preview: true,
  imageMode: 'aspectFill',
  itemSize: '200rpx',
  uploadText: '上传图片',
  customClass: ''
})

// Emits
const emit = defineEmits<{
  'update:modelValue': [fileList: FileItem[]]
  change: [fileList: FileItem[]]
  upload: [file: FileItem]
  remove: [file: FileItem, index: number]
  preview: [file: FileItem, index: number]
  error: [error: string, file: FileItem]
}>()

// 响应式数据
const previewVisible = ref(false)
const previewIndex = ref(0)

// 计算属性
const fileList = computed({
  get: () => props.modelValue,
  set: (value) => emit('update:modelValue', value)
})

const uploadClass = computed(() => {
  return [
    {
      'image-upload--disabled': props.disabled
    },
    props.customClass
  ]
})

const itemStyle = computed(() => {
  return {
    width: props.itemSize,
    height: props.itemSize
  }
})

const showUploadBtn = computed(() => {
  return !props.disabled && fileList.value.length < props.maxCount
})

// 方法
/**
 * 选择图片
 */
const handleUpload = () => {
  if (props.disabled) return

  uni.chooseImage({
    count: props.maxCount - fileList.value.length,
    sizeType: ['compressed'],
    sourceType: ['album', 'camera'],
    success: (res) => {
      const files = res.tempFiles
      files.forEach(file => {
        const fileItem: FileItem = {
          url: file.path,
          name: file.name || `image_${Date.now()}`,
          size: file.size,
          status: 'ready'
        }

        // 检查文件大小
        if (file.size > props.maxSize) {
          fileItem.status = 'error'
          fileItem.error = '文件大小超出限制'
          emit('error', '文件大小超出限制', fileItem)
          return
        }

        // 模拟上传过程
        simulateUpload(fileItem)
      })
    },
    fail: (error) => {
      console.error('选择图片失败:', error)
      uni.showToast({
        title: '选择图片失败',
        icon: 'error'
      })
    }
  })
}

/**
 * 模拟上传过程
 */
const simulateUpload = (fileItem: FileItem) => {
  // 添加到文件列表
  const newFileList = [...fileList.value, fileItem]
  fileList.value = newFileList

  // 开始上传
  fileItem.status = 'uploading'
  fileItem.progress = 0

  // 模拟上传进度
  const timer = setInterval(() => {
    if (fileItem.progress! < 100) {
      fileItem.progress! += 10
    } else {
      clearInterval(timer)
      fileItem.status = 'success'
      emit('upload', fileItem)
      emit('change', fileList.value)
    }
  }, 100)
}

/**
 * 删除图片
 */
const handleRemove = (index: number) => {
  const file = fileList.value[index]
  fileList.value.splice(index, 1)
  emit('remove', file, index)
  emit('change', fileList.value)
}

/**
 * 预览图片
 */
const handlePreview = (file: FileItem, index: number) => {
  if (!props.preview) return

  previewIndex.value = index
  previewVisible.value = true
  emit('preview', file, index)
}

/**
 * 关闭预览
 */
const handleClosePreview = () => {
  previewVisible.value = false
}

/**
 * Swiper 变化
 */
const handleSwiperChange = (e: any) => {
  previewIndex.value = e.detail.current
}
</script>

<style lang="scss" scoped>
.image-upload {
  width: 100%;

  &__list {
    display: flex;
    flex-wrap: wrap;
    gap: 16rpx;
  }

  &__item {
    position: relative;
    border-radius: 12rpx;
    overflow: hidden;
    background: #f5f5f5;
  }

  &__image {
    width: 100%;
    height: 100%;
    border-radius: 12rpx;
  }

  &__mask {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.4);
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 24rpx;
    opacity: 0;
    transition: opacity 0.3s ease;
  }

  &__item:hover &__mask {
    opacity: 1;
  }

  &__action {
    width: 64rpx;
    height: 64rpx;
    background: rgba(255, 255, 255, 0.9);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: all 0.3s ease;

    &:active {
      transform: scale(0.9);
    }

    &--danger {
      background: rgba(255, 77, 79, 0.9);
    }
  }

  &__action-icon {
    font-size: 32rpx;
    color: #333;

    .image-upload__action--danger & {
      color: #fff;
    }
  }

  &__upload {
    border: 2rpx dashed #d9d9d9;
    border-radius: 12rpx;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background: #fafafa;
    cursor: pointer;
    transition: all 0.3s ease;

    &:active {
      background: #f0f0f0;
      border-color: #1890ff;
    }
  }

  &__upload-icon {
    font-size: 64rpx;
    color: #999;
    margin-bottom: 16rpx;
  }

  &__upload-text {
    font-size: 24rpx;
    color: #666;
  }

  &__loading {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.6);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    border-radius: 12rpx;
  }

  &__loading-spinner {
    width: 60rpx;
    height: 60rpx;
    border: 6rpx solid rgba(255, 255, 255, 0.3);
    border-top: 6rpx solid #fff;
    border-radius: 50%;
    animation: spin 1s linear infinite;
    margin-bottom: 16rpx;
  }

  &__loading-text {
    font-size: 24rpx;
    color: #fff;
  }

  &__error {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(255, 77, 79, 0.8);
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 12rpx;
  }

  &__error-text {
    font-size: 24rpx;
    color: #fff;
  }

  &--disabled {
    .image-upload__upload {
      cursor: not-allowed;
      opacity: 0.6;

      &:active {
        background: #fafafa;
        border-color: #d9d9d9;
      }
    }
  }
}

.image-preview {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.9);
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;

  &__swiper {
    width: 100%;
    height: 100%;
  }

  &__item {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
  }

  &__image {
    max-width: 100%;
    max-height: 100%;
  }

  &__counter {
    position: absolute;
    bottom: 40rpx;
    left: 50%;
    transform: translateX(-50%);
    background: rgba(0, 0, 0, 0.6);
    color: #fff;
    padding: 16rpx 32rpx;
    border-radius: 32rpx;
    font-size: 24rpx;
  }
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
</style>